//xlang Source, Name:BusinessProxy.x 
//Date: Mon Oct 13:32:37 2024 


class CommAsync : AsyncInput{
    public bool newInstance(AsyncOutput output, int type, long bind, long context)override{ return true; }
    public bool dataArrives(AsyncOutput output, long context, byte[] data, int offset, int length)override{ return false; }
    public bool deleteInstance(AsyncOutput output, long context)override{ return true; }
    public bool inactiveTimeout(AsyncOutput output, long context)override{ return true;}
};

class BusinessProxy{
    static String proxy_serverHost;
    static int proxy_proxyPort, proxy_idlePort, proxy_localPort;
    static int timeout = 90000, buffersize = 1024, thread = 2;
    
    public static bool loadConfigure(){
        FileStream.FileInputStream fis = nilptr;
        try{
            File fp = new File(_system_.getAppDirectory().appendPath("proxy_proxy.json"));
            if (fp.exists()){
                fis =  new FileStream.FileInputStream(fp);
                JsonObject json = new JsonObject(new String(fis.read()));
                proxy_serverHost = json.getString("proxy_serverHost");
                proxy_proxyPort = json.getInt("proxy_proxyPort");
                proxy_localPort = json.getInt("proxy_localPort");
                proxy_idlePort = json.getInt("proxy_idlePort");
                if (json.has("timeout")){
                    timeout = json.getInt("timeout");
                }
                if (json.has("buffersize")){
                    buffersize = json.getInt("buffersize");
                }
                if (json.has("seed")){
                    seed = json.getInt("seed");
                }
                if (buffersize < 1024){
                    buffersize = 1024;
                }
                if (json.has("thread")){
                    thread = json.getInt("thread");
                }
                if (thread < 1){
                    thread = 1;
                }
                return proxy_serverHost != nilptr && proxy_proxyPort != 0 && proxy_idlePort != 0;
            }
        }catch(Exception e){
        
        }finally{
            if (fis != nilptr){
                fis.close();
            }
        }
        return false;
    }
    
	Unsi _unsi_local = new Unsi();
    Unsi _unsi_server = new Unsi();
    
    Object lock = new Object();
    
    static byte seed = 8;
    
    static void encrypt(byte [] data,int offset, int len){
        for (int i : len){
            data[offset + i] = data[offset + i] ^ 8;
        }
    }
    
    Map<int, long> proxy_local = new Map<int, long>();
    Map<int, int> proxy_map = new Map<int, int>();
    Map<int, long> proxy_server = new Map<int, long>();
    Map<int, ByteArrayOutputStream> id_buffer = new Map<int, ByteArrayOutputStream>();// 服务器ID, 缓存
    
    long getLocal(int id){
        var iter = proxy_map.find(id);
        if (iter != nilptr){
            var sit = proxy_local.find(iter.getValue());
            if (sit != nilptr){
                return sit.getValue();
            }
        }
        return 0l;
    }
    
    long getServer(int id){
        var iter = proxy_map.find(id);
        if (iter != nilptr){
            var sit = proxy_server.find(iter.getValue());
            if (sit != nilptr){
                return sit.getValue();
            }
        }
        return 0l;
    }
    
    public void bind_id(int sid, int lid, long s_ctx){
        ByteArrayOutputStream bos;
        synchronized (lock) {
            if (sid != 0){
                proxy_map.put(sid, lid);
                proxy_map.put(lid, sid);
                if (id_buffer.containsKey(lid)){
                    bos =  id_buffer.get(lid);
                }
            }
            id_buffer.remove(lid);
        }
        
        if (bos != nilptr){
            if (sid != 0){
                byte [] data = bos.toByteArray();
                if (data.length > 0){
                    _unsi_server.getAsyncDirectOutput().dataDeparture(serverInput, s_ctx,0,data, 0,data.length);
                }
            }else{
                closeLocal(lid);
            }
            bos.close();
        }
    }
    
    AsyncInput localInput = new CommAsync(){
        // 本地接入新连接 
        bool newInstance(AsyncOutput output, int type, long bind, long context)override{ 
            int id = output.getInstanceId(context);
            synchronized (lock) {
                id_buffer.put(id,new ByteArrayOutputStream());
                long _ctx = output.cloneContext(context);
                proxy_local.put(id, _ctx);
            }
            _unsi_server.getAsyncDirectOutput().newInstance(this,proxy_serverHost, proxy_proxyPort, id); 
            return true;
        }
        
        bool dataArrives(AsyncOutput output, long context, byte[] data, int offset, int length)override{ 
            encrypt(data, offset, length);  // 加密 然后传送
            synchronized (lock) {
                int id = output.getInstanceId(context);
                long sctx = getServer(id);
                if (sctx != 0){
                    _unsi_server.getAsyncDirectOutput().dataDeparture(this, sctx,0,data,offset,length);
                }else
                if (id_buffer.containsKey(id)){
                    ByteArrayOutputStream bos =  id_buffer.get(id);
                    bos.write(data, offset, length);
                }
            }
            return true;
        }
        
        bool deleteInstance(AsyncOutput output, long context)override{ 
            int id = output.getInstanceId(context);
            synchronized (lock) {
                if (proxy_local.containsKey(id)){ // 做自己的清理工作
                    long s_ctx = proxy_local.get(id);
                    proxy_local.remove(id);
                    output.releaseContext(s_ctx);
                }
                if (proxy_map.containsKey(id)){ // 关闭关联
                    int pid = proxy_map.get(id);
                    proxy_map.remove(id);
                    proxy_map.remove(pid);
                    closeServer(pid);
                }
            }
            return true;
        }
    };
    
    AsyncInput serverInput = new CommAsync(){
        void notify(AsyncOutput output, int type, long bind, bool bsucceed, long context,int length)override{
            // type 1 为新建连接， 没有成功的时候, 清除缓冲器，并阻断连接
            if (type == 1 && bsucceed == false){
                bind_id(0, bind, context);
            }
        }
        
        public bool newInstance(AsyncOutput output, int type, long bind, long context)override{ 
            int id = output.getInstanceId(context);
            synchronized (lock) {
                long _ctx = output.cloneContext(context);
                proxy_server.put(id, _ctx);
                bind_id(id, bind, _ctx);
            }
            return true;  
        }
        
        bool dataArrives(AsyncOutput output, long context, byte[] data, int offset, int length)override{ 
            encrypt(data, offset, length);  // 加密 然后传送
            synchronized (lock) {
                long sctx = getLocal(output.getInstanceId(context));
                if (sctx != 0){
                    _unsi_local.getAsyncDirectOutput().dataDeparture(this, sctx,0,data,offset,length);
                }
            }
            return true;
        }
        
        bool deleteInstance(AsyncOutput output, long context)override{ 
            int id = output.getInstanceId(context);
            synchronized (lock) {
                if (proxy_server.containsKey(id)){  // 做自己的清理工作
                    long s_ctx = proxy_server.get(id);
                    proxy_server.remove(id);
                    output.releaseContext(s_ctx);
                }
                if (proxy_map.containsKey(id)){  // 关闭关联
                    int pid = proxy_map.get(id);
                    proxy_map.remove(id);
                    proxy_map.remove(pid);
                    closeLocal(pid);
                }
            }
            return true;
        }
    };
    
        
    public bool start(){
        // 本地 
		if (_unsi_local.create(localInput, Unsi.UNSI_TCP, proxy_localPort, timeout) == false){
            return false;
		} 
        
        if (_unsi_server.create(serverInput, Unsi.UNSI_TCP, proxy_idlePort, timeout) == false){
            return false;
		} 
        
        _unsi_local.config(thread, buffersize, timeout);
        _unsi_server.config(thread, buffersize, timeout);
        return _unsi_server.start() && _unsi_local.start();
    }
    
    void closeLocal(int id){
        if (proxy_local.containsKey(id)){
            long ctx = proxy_local.get(id);
            _unsi_local.getAsyncDirectOutput().deleteInstance(localInput, ctx);
        }
    }
    
    void closeServer(int id){
        if (proxy_server.containsKey(id)){
            long ctx = proxy_server.get(id);
            _unsi_server.getAsyncDirectOutput().deleteInstance(serverInput, ctx);
        }
    }
    
};